package mips.instructions;

import mips.Main;

/**
 * <code>BEQL</code> instruction<br/>
 * Branch On Equal Likely<br/>
 * @author jnmartin84@gmail.com
 */
public class BEQL extends Instruction {

	private static final BEQL INSTANCE = new BEQL();
	private static final String INSTRUCTION_NAME = "BEQL";

	private BEQL(){}

	public static final BEQL getInstance() {
		return INSTANCE;
	}

	/**
	 * <b>Format:</b><br/>
	 * BEQL rs, rt, offset<br/><br/>
	 * <b>Description:</b><br/>
	 * A branch target address is computed from the sum of the address of the<br/>
	 * instruction in the delay slot and the 16-bit offset, shifted left two bits and<br/>
	 * sign-extended. The contents of general register rs and the contents of<br/>
	 * general register rt are compared. If the two registers are equal, the target<br/>
	 * address is branched to, with a delay of one instruction. If the conditional<br/>
	 * branch is not taken, the instruction in the branch delay slot is nullified.<br/><br/>
	 * <b>Operation:</b><br/>
	 * T: target &larr; (offset<sub>15</sub>)<sup>14</sup> || offset || 0<sup>2</sup><br/>
	 * condition &larr; (GPR[rs] = GPR[rt])<br/>
	 * T+1: if condition then<br/>
	 * PC &larr; PC + target<br/>
	 * else<br/>
	 * endif<br/>
	 * NullifyCurrentInstruction<br/>
	 */
	@Override
	public final void execute(final int instruction) {
	
		/**
		 * from: http://www.pagetable.com/?p=313
		 * 
		 * "Some RISCs like PowerPC and ARM do not have a delay slot, 
		 * but for example MIPS, SPARC, PA-RISC have it. But there are 
		 * some variations: MIPS and PA-RISC have an annihilation/nullify/likely 
		 * bit in the instruction, so the programmer can choose that the 
		 * instruction in the delay slot only gets executed if the branch is taken."
		 */
		final int rs = (instruction >> 21) & 0x0000001F;
		final int rt = (instruction >> 16) & 0x0000001F;

		if (mips.R4300i.GPR[rs] == mips.R4300i.GPR[rt]) {

			final int offset = Instruction.signExtendH((instruction) & 0x0000FFFF) << 2;

			mips.R4300i.PC = mips.R4300i.nPC;
			mips.R4300i.nPC = mips.R4300i.PC + offset;
			
			if(Main.tracing) {
				mips.R4300i.targets.put(mips.R4300i.nPC,null);
			}
		} 
		else {

			mips.R4300i.PC = mips.R4300i.nPC + 4;
			mips.R4300i.nPC = mips.R4300i.PC + 4;
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String emit(final int instruction) {
		
		final int rs = (instruction >> 21) & 0x0000001F;
		final int rt = (instruction >> 16) & 0x0000001F;
		final int offset = Instruction.signExtendH(instruction & 0x0000FFFF) << 2;
		
		return	"		if (mips.CPU.GPR["+rs+"] == mips.CPU.GPR["+rt+"]) {\n" + 
				"			mips.CPU.PC = mips.CPU.nPC;\n" + 
				"			mips.CPU.nPC = mips.CPU.PC + "+offset+";\n" + 
				"		} \n" + 
				"		else {\n" + 
				"			\n" + 
				"			mips.CPU.PC = mips.CPU.nPC + 4;\n" + 
				"			mips.CPU.nPC = mips.CPU.PC + 4;\n" + 
				"			skipDelaySlot = true;\n" +
				"		}\n";
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName(final int instruction) {
		return getName();
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName() {
		return INSTRUCTION_NAME;
	}
}